// package: rewords-server-2024
// filename: apptoken
// author: diogoxiang@gmail.com
// time: 2024/11/20 11:27

package apptoken

import (
	"context"
	"fmt"
	"time"

	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/gogf/gf/v2/util/grand"

	"rewords-server-2024/internal/consts"
	"rewords-server-2024/internal/library/cache"
	"rewords-server-2024/internal/model"
	"rewords-server-2024/internal/model/entity"
	"rewords-server-2024/utility/ufun"
)

// Token 定义令牌结构
type Token struct {
	UserId   int64  `json:"uid"` //用户uid
	App      string `json:"app"`
	Status   int    `json:"status"` // 状态
	Points   int64  `json:"points"`
	Role     string `json:"role"`  // 角色
	Level    int64  `json:"level"` // 等级
	ExpireAt int64  `json:"exp"`   // token过期时间
}
type SysAdminToken struct {
	UserId   int64  `json:"uid"` // 后台的用户ID
	App      string `json:"app"`
	Status   int    `json:"status"` // 状态
	Points   int64  `json:"points"`
	Role     string `json:"role"`  // 角色
	Level    int64  `json:"level"` // 等级
	ExpireAt int64  `json:"exp"`   // token过期时间
}

var (
	config     *model.TokenConf
	errorToken = gerror.New("invalid token or expired")
)

// SetConfig 设置配置
func SetConfig(c *model.TokenConf) {
	config = c
}

// GenerateToken 生成token
func GenerateToken(ctx context.Context, user *entity.SysUser) (string, int64, error) {
	// 生成随机token
	tokenStr := grand.Letters(32)

	// 缓存token信息
	tokenData := &Token{
		UserId:   user.Uid, // TODO: 暂时用uid
		Status:   user.Status,
		Role:     user.Role,
		Level:    user.Level,
		Points:   user.Points,
		App:      user.App,
		ExpireAt: time.Now().Unix() + config.Expires,
	}

	cacheKey := fmt.Sprintf("token:%s", tokenStr)
	if err := cache.Instance("user").Set(ctx, cacheKey, tokenData, time.Second*gconv.Duration(config.Expires)); err != nil {
		return "", 0, err
	}

	return tokenStr, config.Expires, nil
}

// ParseToken 解析并验证token
func ParseToken(ctx context.Context, tokenStr string) (*entity.SysUser, error) {
	cacheKey := fmt.Sprintf("token:%s", tokenStr)
	tk, err := cache.Instance("user").Get(ctx, cacheKey)
	if err != nil || tk.IsEmpty() {
		return nil, errorToken
	}

	var tokenData *Token
	if err = tk.Scan(&tokenData); err != nil {
		return nil, errorToken
	}

	if tokenData.ExpireAt < time.Now().Unix() {
		return nil, errorToken
	}

	return &entity.SysUser{
		Uid:    tokenData.UserId,
		Status: tokenData.Status,
		Role:   tokenData.Role,
		Level:  tokenData.Level,
		App:    tokenData.App,
		Points: tokenData.Points,
	}, nil
}

// RemoveToken 删除token
func RemoveToken(ctx context.Context, tokenStr string) error {
	cacheKey := fmt.Sprintf("token:%s", tokenStr)
	_, err := cache.Instance("user").Remove(ctx, cacheKey)
	return err
}

func GenerateAdminToken(ctx context.Context, user *entity.SysAdminUser) (string, int64, error) {
	// 生成随机token
	tokenStr := grand.Letters(32)

	// 缓存token信息
	tokenData := &SysAdminToken{
		UserId: user.Uid, // TODO: 暂时用uid
		Role:   user.Role,
		Status: user.Status,
		//App:      user.App,
		ExpireAt: time.Now().Unix() + config.Expires,
	}

	cacheKey := fmt.Sprintf("token:%s", tokenStr)
	if err := cache.Instance("user").Set(ctx, cacheKey, tokenData, time.Second*gconv.Duration(config.Expires)); err != nil {
		return "", 0, err
	}

	return tokenStr, config.Expires, nil

}

func ParseAdminToken(ctx context.Context, tokenStr string) (*entity.SysAdminUser, error) {

	cacheKey := fmt.Sprintf("token:%s", tokenStr)
	tk, err := cache.Instance("user").Get(ctx, cacheKey)
	if err != nil || tk.IsEmpty() {
		return nil, errorToken
	}

	var tokenData *SysAdminToken
	if err = tk.Scan(&tokenData); err != nil {
		return nil, errorToken
	}
	if tokenData.ExpireAt < time.Now().Unix() {
		return nil, errorToken
	}
	return &entity.SysAdminUser{
		Uid:    tokenData.UserId,
		Role:   tokenData.Role,
		Status: tokenData.Status,
		//App: tokenData.App,
	}, nil
}

// GetAuthorization 获取authorization
func GetAuthorization(r *ghttp.Request) string {
	// 默认从请求头获取
	var authorization = ufun.If(r.Header.Get("Authorization") == "", r.Header.Get(consts.RequestHeaderAdminToken), r.Header.Get("Authorization"))

	// 如果请求头不存在则从get参数获取
	if authorization == "" {
		return r.Get(consts.RequestToken).String()
	}

	return gstr.Replace(authorization, "Bearer ", "")
}

// ParseLoginUser 解析登录用户
func ParseLoginUser(r *ghttp.Request) (user *model.Identity, err error) {
	var (
		ctx      = r.Context()
		CurToken = GetAuthorization(r)
	)
	if CurToken == "" {
		err = errorToken
	}
	cacheKey := fmt.Sprintf("token:%s", CurToken)
	// 检查token是否存在
	tk, err := cache.Instance("user").Get(ctx, cacheKey)
	if err != nil {
		g.Log().Debugf(ctx, "get tokenKey err:%+v", err)
		err = errorToken
		return
	}

	if tk.IsEmpty() {
		g.Log().Debug(ctx, "token isEmpty")
		err = errorToken
		return
	}
	var token *Token
	if err = tk.Scan(&token); err != nil {
		g.Log().Debugf(ctx, "token scan err:%+v", err)
		err = errorToken
		return
	}

	if token == nil {
		g.Log().Debugf(ctx, "token = nil")
		err = errorToken
		return
	}

	now := gtime.Now()
	if token.ExpireAt < now.Unix() {
		g.Log().Debugf(ctx, "token expired.")
		err = errorToken
		return
	}

	user = &model.Identity{
		Uid:     token.UserId,
		App:     token.App,
		Status:  token.Status,
		Role:    token.Role,
		Level:   token.Level,
		Points:  token.Points,
		Expires: token.ExpireAt - now.Unix(),
	}

	return
}
